package cn.com.surker.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import sun.font.FontDesignMetrics;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * ZXingUtils 二维码生成工具
 *
 */
public class ZXingUtils {
    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;

    /**
     * 创建二维码
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @return 缓冲图片
     */
    public static BufferedImage createQRCodeImage(String content, int width, int height) {
        // 用于设置QR二维码参数
        Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
        // 设置QR二维码的纠错级别（H为最高级别）具体级别信息
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 设置编码方式
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = new MultiFormatWriter().encode(content,
                    BarcodeFormat.QR_CODE, width, height, hints);

            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE);
                }
            }
            return image;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建带文字的二维码
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @param bottomText    写入文字
     * @return 缓冲图片
     */
    public static BufferedImage createQRCodeWithTip(String content, int width, int height, String bottomText) {
        if (content == null) {
            throw new RuntimeException("未包含任何信息");
        }
        height += 30;
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");    //定义内容字符集的编码
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);        //定义纠错等级
        hints.put(EncodeHintType.MARGIN, 1);
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        try {
            BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);

            width = bitMatrix.getWidth();
            height = bitMatrix.getHeight();
            int tempHeight = height;
            if (!StringUtils.isNull(bottomText)) {
                tempHeight = tempHeight + 12;
            }
            BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
                }
            }
            // 判断是否添加底部文字
            if (!StringUtils.isNull(bottomText)) {
                addFontImage(image, width, height, bottomText);
            }
            return image;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static void addFontImage(BufferedImage source, int width, int height, String declareText) {
        //生成image
        int defineWidth = width;
        int defineHeight = 32;
        BufferedImage textImage = new BufferedImage(defineWidth, defineHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) textImage.getGraphics();
        //开启文字抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.setBackground(Color.WHITE);
        g2.clearRect(0, 0, defineWidth, defineHeight);
        g2.setPaint(Color.BLACK);
        FontRenderContext context = g2.getFontRenderContext();
        Font font = new Font("宋体", Font.BOLD, 28);
        g2.setFont(font);
        LineMetrics lineMetrics = font.getLineMetrics(declareText, context);
        FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
        float offset = (defineWidth - fontMetrics.stringWidth(declareText)) / 2;
        float y = (defineHeight + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2;
        g2.drawString(declareText, (int) offset, (int) y);

        Graphics2D graph = source.createGraphics();
        //开启文字抗锯齿
        graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,   RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        //添加image
        width = textImage.getWidth(null);
        height = textImage.getHeight(null);

        Image src = textImage;
        graph.drawImage(src, 0, width - 8, width, height + 20, Color.WHITE, null);
        graph.dispose();
    }

    /**
     * 创建带Logo的二维码
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @param logo    logo输入流
     * @return 缓冲图片
     */
    public static BufferedImage createQRCodeWithLogo(String content, int width, int height, InputStream logo) {
        BufferedImage image = createQRCodeImage(content, width, height);
        Graphics2D graphics = image.createGraphics();
        // logo
        try {
            BufferedImage logoImage = ImageIO.read(logo);
            // 缩放
            int widthLogo = logoImage.getWidth(null) > image.getWidth() * 2 / 10 ? (image.getWidth() * 2 / 10) : logoImage.getWidth(null);
            int heightLogo = logoImage.getHeight(null) > image.getHeight() * 2 / 10 ? (image.getHeight() * 2 / 10) : logoImage.getWidth(null);

            int x = (image.getWidth() - widthLogo) / 2;
            int y = (image.getHeight() - heightLogo) / 2;
            graphics.drawImage(logoImage, x, y, widthLogo, heightLogo, null);
            graphics.dispose();
            image.flush();
            return image;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                logo.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 创建带Logo的二维码
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @param logo    logo字节数组
     * @return 缓冲图片
     */
    public static BufferedImage createQRCodeWithLogo(String content, int width, int height, byte[] logo) {
        InputStream in = new ByteArrayInputStream(logo);
        return createQRCodeWithLogo(content, width, height, in);
    }

    /**
     * 创建带Logo的二维码
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @param logo    logo文件
     * @return 缓冲图片
     */
    public static BufferedImage createQRCodeWithLogo(String content, int width, int height, File logo) {
        try {
            InputStream in = new FileInputStream(logo);
            return createQRCodeWithLogo(content, width, height, in);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 图片转字节数组
     *
     * @param image  二维码缓冲图片
     * @param format 图片格式
     * @return 字节数组
     */
    public static byte[] imageToBytes(BufferedImage image, String format) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, format, out);
            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new byte[0];
    }

    /**
     * 生成带LOGO的二维码
     *
     * @param qrCode 二维码字节
     * @param logo   LOGO字节
     * @return
     */
    public static byte[] createQRCodeWithLogo(byte[] qrCode, byte[] logo) {
        try {
            BufferedImage image = ImageIO.read(new ByteArrayInputStream(qrCode));
            Graphics2D graphics = image.createGraphics();

            BufferedImage logoImage = ImageIO.read(new ByteArrayInputStream(logo));

            // 缩放
            int widthLogo = logoImage.getWidth(null) > image.getWidth() * 2 / 10 ? (image.getWidth() * 2 / 10) : logoImage.getWidth(null);
            int heightLogo = logoImage.getHeight(null) > image.getHeight() * 2 / 10 ? (image.getHeight() * 2 / 10) : logoImage.getWidth(null);

            int x = (image.getWidth() - widthLogo) / 2;
            int y = (image.getHeight() - heightLogo) / 2;
            graphics.drawImage(logoImage, x, y, widthLogo, heightLogo, null);
            graphics.dispose();
            image.flush();

            return imageToBytes(image, "png");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}